package com.sun.spring.servlet;

import com.sun.demo.mvc.action.DemoAction;
import com.sun.spring.annotation.Autowired;
import com.sun.spring.annotation.Controller;
import com.sun.spring.annotation.Service;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * create by Sun
 * 2018/8/9
 */
public class DispatchServlet extends HttpServlet {
    private Properties contextConfig=new Properties();
    
    private Map<String,Object> beanMap=new ConcurrentHashMap<String,Object>();
    
    private List<String> classNames=new ArrayList<String>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("-----------调用doPost方法-----------");
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //开始初始化的进程
        
        //定位
        doLoadConfig(config.getInitParameter("contextConfigLocation"));

        //加载 
        doScanner(contextConfig.getProperty("scanPackage"));
        //注册
        doRegistzry();
        //自动依赖注入
        
        //在spring中是通过调用getBean方法才翻出依赖注入的
        doAutowired();
        /**
         * 测试用例
         */

          //如果是SpringMVC会多设计一个HnandlerMapping
        DemoAction action = (DemoAction)beanMap.get("demoAction");
        action.query(null,null,"Mst SUN");
        //将@RequestMapping中配置的url和一个Method关联上
        //以便于从浏览器获得用户输入的url以后，能够找到具体执行的Method通过反射去调用
        initHandlerMapping();

    }

    private void initHandlerMapping() {
    }

    /**
     * 扫描 各个包
     * 利用递归的方法遍历所有的包
     * @param packageName
     */
    private void doScanner(String packageName) {
        URL url=this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.","/"));
        File classDir = new File(url.getFile());
        for(File file:classDir.listFiles()){
            if(file.isDirectory()){
                doScanner(packageName+"."+file.getName());
            }else{
               classNames.add(packageName+"."+file.getName().replaceAll(".class",""));
            }
        }

    }

    /**
     * 注册的功能实现
     */
    private void doRegistzry() {
        if(classNames.isEmpty()){
            return ;
        }
        try {
        for(String className:classNames){

                Class<?> clazz=Class.forName(className);

                if(clazz.isAnnotationPresent(Controller.class)){
                    String beanName=lowerFirstCase(clazz.getSimpleName());


                    //Spring 中在这个阶段是不会是直接put instance 这里put的是 beanDefination
                        beanMap.put(beanName,clazz.newInstance());
                }else if(clazz.isAnnotationPresent(Service.class)){
                        Service service=clazz.getAnnotation(Service.class);
                        //默认用类名首字母注入
                        //如果自己定义了beanName，那么优先使用自己的定义的BeanName
                    //如果是一个接口。使用借口的类型去自动注入
                    //在spring中的bean的ioc，依旧课一使用auwowiredByname Bytype
                    /**
                     *  service.value() 就是你所设置的String value()的值
                     */
                    String beanName=service.value();
                    if("".equals(beanName.trim())){
                        /**
                         * 否则就是 首字母小写
                         */
                        beanName=lowerFirstCase(clazz.getSimpleName());
                    }
                    Object instance=clazz.newInstance();
                    beanMap.put(beanName,instance);

                    /**
                     * 需要把所实现的接口也 注入进去
                     */
                    Class<?>[] interfaces = clazz.getInterfaces();

                    for (Class<?> i : interfaces){
                        beanMap.put(i.getName(),instance);
                    }
                 }else{
                    continue;
                }

        }
        } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    /**
     * 对象的 首字母小写
     * @param simpleName
     * @return
     */
    private String lowerFirstCase(String simpleName) {
        char[] chars = simpleName.toCharArray();
        chars[0]+=32;
        return String.valueOf(chars);
    }

    /**
     * 自动注入
     */
    private void doAutowired() {
        if (beanMap.isEmpty()) {
            return;
        }
        Set<Map.Entry<String, Object>> entries = beanMap.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            /**
             * 利用反射获取，这个类的信息，所有的变量
             */
            Field[] fields = entry.getValue().getClass().getDeclaredFields();

            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class) == false) { continue;}

                Autowired autowired = field.getAnnotation(Autowired.class);

                String beanName=autowired.value().trim();
                if("".equals(beanName)){
                    /**
                     * 获取变量的类的 名称
                     */
                    beanName=field.getType().getName();
                    System.out.println("field.getType().getName() ->"+beanName);
                }
                field.setAccessible(true);
                /**
                 *
                 */
                try {
                    field.set(entry.getValue(),beanMap.get(beanName));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        }
    }
    private void doLoadConfig(String location) {
        //在Spring中是通过Reader去查找和定位对不对
        InputStream is = this.getClass().getClassLoader().getResourceAsStream(location.replace("classpath:",""));

        try {
            contextConfig.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(null != is){is.close();}
            }catch (Exception e){
                e.printStackTrace();
            }

        }


    }

}
